#pragma once

#include <iostream>
#include <cmath>
#include "vec2.h"

struct Vec3
{
	float  x;
	float  y;
	float  z;

	Vec3( float s = float(0.0) ) :
		x(s), y(s), z(s)
	{
		//
	}

	Vec3( float x, float y, float z ) :
		x(x), y(y), z(z)
	{

	}

	Vec3( const Vec3& v )
	{
		x = v.x;
		y = v.y;
		z = v.z;
	}

	Vec3( const Vec2& v, const float f )
	{
		x = v.x;
		y = v.y;
		z = f;
	}

	float& operator [] ( int i ) { return *(&x + i); }
	const float operator [] ( int i ) const { return *(&x + i); }

	float dot(const Vec3& v ) const
	{
		return x*v.x + y*v.y + z*v.z ;
	}

	float length() const
	{
		return std::sqrt( dot(*this) );
	}

	Vec3& normalize()
	{
		*this /= length();
		return *this;
	}

	Vec3 cross( const Vec3& v ) const
	{
		return Vec3(
			y*v.z-z*v.y,
			z*v.x-x*v.z,
			x*v.y-y*v.x
		);
	}

	Vec3 operator - () const
	{
		return Vec3( -x, -y, -z );
	}

	Vec3 operator + ( const Vec3& v ) const
	{
		return Vec3( x + v.x, y + v.y, z + v.z );
	}

	Vec3 operator - ( const Vec3& v ) const
	{
		return Vec3( x - v.x, y - v.y, z - v.z );
	}

	Vec3 operator * ( const float s ) const
	{
		return Vec3( s*x, s*y, s*z );
	}

	Vec3 operator * ( const Vec3& v ) const
	{
		return Vec3( x*v.x, y*v.y, z*v.z );
	}

	friend Vec3 operator * ( const float s, const Vec3& v )
	{
		return v * s;
	}

	Vec3 operator / ( const float s ) const
	{
		float r = float(1.0) / s;
		return *this * r;
	}

	Vec3& operator += ( const Vec3& v )
	{
		x += v.x;
		y += v.y;
		z += v.z;
		return *this;
	}

	Vec3& operator -= ( const Vec3& v )
	{
		x -= v.x; 
		y -= v.y;
		z -= v.z;
		return *this;
	}

	Vec3& operator *= ( const float s )
	{
		x *= s;  
		y *= s;
		z *= s;
		return *this;
	}

	Vec3& operator *= ( const Vec3& v )
	{
		x *= v.x;
		y *= v.y; 
		z *= v.z;
		return *this;
	}

	Vec3& operator /= ( const float s )
	{
		float r = float(1.0) / s;
		*this *= r;

		return *this;
	}

	friend std::ostream& operator << ( std::ostream& os, const Vec3& v )
	{
		return os << "( " << v.x << ", " << v.y << ", " << v.z <<  " )";
	}

	friend std::istream& operator >> ( std::istream& is, Vec3& v )
	{
		return is >> v.x >> v.y >> v.z ;
	}

	operator const float* () const
	{
		return static_cast<const float*>( &x );
	}

	operator float* ()
	{
		return static_cast<float*>( &x );
	}
};
